﻿using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;

namespace ZerAx.Net
{
    public class UdpConnector : IConnector
    {
        public Socket udpListener;

        private readonly Dictionary<EndPoint, InnerUdpConnection> connections = new();
        private readonly byte[] buffer;
        
        private readonly Queue<InnerUdpConnection> unsubmittedConnections = new();
        private bool connectionUpdateFlag;

        public UdpConnector(IPEndPoint ep)
        {
            udpListener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            udpListener.Bind(ep);
            udpListener.Blocking = false;

            buffer = new byte[4096];
        }
        public UdpConnector(IPAddress address, int port) : this(new IPEndPoint(address, port)) { }
        public UdpConnector(long address, int port) : this(new IPEndPoint(address, port)) { }

        public UdpConnector(string ipAddress, int port) : this(new IPEndPoint(IPAddress.Parse(ipAddress), port)) { }

        public UdpConnector(string ipAddressWithPort) : this(ipAddressWithPort.ToIPEndPoint()) { }

        public void Init()
        {
            
        }

        public void Close()
        {
            /*foreach (var conn in connections.Values)
            {
                conn.isConnecting = false;
            }*/
            udpListener.Close();
        }

        public bool TryProduceConnection(out IConnection connection)
        {
            connection = null;
            
            if (unsubmittedConnections.Count > 0)
            {
                connection = unsubmittedConnections.Dequeue();
                return true;
            }
            else
            {
                UpdateConnections();
                return false;
            }
        }

        private void UpdateConnections()
        {
            while (udpListener.Poll(0, SelectMode.SelectRead))
            {
                EndPoint ep = new IPEndPoint(0, 0);
                int len = udpListener.ReceiveFrom(buffer, ref ep);
                if (!connections.ContainsKey(ep))
                {
                    UnityEngine.Debug.Log($"New {ep} added.");
                    var conn = new InnerUdpConnection(udpListener, (IPEndPoint)ep);
                    conn.Write(buffer, 0, len);
                    connections.Add(ep, conn);
                    unsubmittedConnections.Enqueue(conn);
                }
                else
                {
                    UnityEngine.Debug.Log($"Old {ep} updated.");
                    connections[ep].Write(buffer, 0, len);
                }
            }

            /*Stack<EndPoint> garbages = new();
            foreach (var (ep, conn) in connections)
            {
                if (!conn.IsConnecting)
                {
                    garbages.Push(ep);
                }
            }

            while (garbages.Count > 0)
            {
                connections.Remove(garbages.Pop());
            }*/
        }
    }
}
